1. Field of Invention
The invention relates generally to computer systems in which two or more program modules, each having internally prelinked portions, are loaded into system memory and further in which, either at the time of loading or during a subsequent run-time of a given module, further links are established from the given module to an external module.
The invention relates more specifically to the problem of how to perform subclassing for an operating system that operates with modules conforming to the portable-executable (PE) format or to a like format wherein modules have import and export data sections that are consulted for purposes of creating inter-module links.
2. Cross Reference to Related Publications
The following publications are believed to be loosely related to the present application and are cited here for purposes of reference and background:
(A) M. Pietrek, "Peering Inside the PE: A Tour of the Win32 Portable Executable File Format", Microsoft Systems Journal, March 1994, pp 15-34; and
(B) M. Pietrek, "Learn System_Level Win32 Coding Techniques by Writing an API Spy Program", Microsoft Systems Journal, December 1994, pp 17-44.
3. Description of Related Art
The term `subclassing` refers generically to a method wherein one starts with a predefined computer operating system (OS), or another predefined program, that has predefined, callable services and one thereafter adds one or more new features to the operating system (or to another like program) such that, when one of the predefined services of the OS (or of the other like program) is requested by an external application program, the newly added features will be executed in place of or in addition to the original services.
Traditional subclassing relies on an operation referred to here as `global vector-table thunking`.
In `global vector-table thunking`, an alteration is made to a globally-constant location of memory through which all application programs normally gain access to services of the memory-resident OS.
The alteration to this globally-constant location forces global redirection of the normal service calls of all application programs to a new location of memory rather than to the location originally dictated by the globally-constant memory-location.
In terms of a more concrete example, consider the Microsoft DOS.TM. operating system that was established for 8086 class and higher microprocessors (e.g., 286, 386, 486, etc. versions of which are available from Intel Corporation of Santa Clara, Calif.).
Consider how application programs normally interface with MS-DOS.TM. through its API.
(API stands for application program interface. Microsoft DOS.TM. and its alternate designation, MS-DOS.TM. are recognized trademarks of Microsoft Corporation of Redmond, Wash.)
Under Microsoft DOS.TM., application programs normally access predefined operating system services by way of software interrupts (INT's in general, and more commonly through a software interrupt known as INT.sub.-- 21).
Each software interrupt (INT) vectors to a respective and globally-constant location in a lower address region of system memory. This region is sometimes referred to as the `Interrupt Vector Table` (IVT). Here, the CPU finds a new address or vector to which the CPU automatically branches when the INT is executed.
During boot-up or other initialization of MS-DOS.TM., locations within the Interrupt Vector Table (IVT) are filled with vectors that point to predefined entry points of respective interrupt service routines found in the main body of MS-DOS.TM..
Thereafter, when an application program executes a software interrupt (e.g., INT.sub.-- 21)--after having set up appropriate values in the CPU registers--the corresponding interrupt vector in the IVT re-directs control to another location in system memory that contains the predefined interrupt service routine.
The interrupt service routine for INT.sub.-- 21 normally includes a dispatcher that redirects program flow to an entry point of a particular executable service module within the main body of the operating system in accordance with request flags indicated by the register settings of the interrupting application program.
The above, indirect approach of getting to MS-DOS.TM. service procedures through an IVT rather than going to directly to such service procedures is used so that newer versions of the operating system can be introduced while maintaining backward compatibility with application programs written for older versions of the OS.
To subclass within MS-DOS.TM. or a like operating system environment, a `vector-table thunk` is performed on a given INT vectoring location within the globally-constant Interrupt Vector Table.
`Vector-table thunking` in this case simply means, overwriting the pre-initialized data within the globally-constant location of the given INT (e.g., the vector storing location of INT.sub.-- 21) with a new address value that redirects control to a new interrupt service routine. The new interrupt service routine may then dispatch (redirect program flow) to one or more newly-added subclassing procedures as desired.
From the above it is seen that subclassing of the OS is relatively trivial to carry out within an operating system environment such as that of Microsoft DOS.TM.. Because API service calls are normally made through predefined, globally-constant locations of system memory, a simple thunk globally re-directs all calls to the desired new location.
When one works within other kinds of operating system environments where there is no globally-constant vectoring table (e.g., as is the case for the Win32 subsystem of Windows95.TM.), one cannot rely on simple vector-table thunking for performing subclassing.
(Windows95.TM. is a recognized trademark of Microsoft Corporation of Redmond, Wash. Win32 is an industry-standardized operating system specification that is supported by Microsoft Corporation.)
These other kinds of operating system environments (e.g., the Win32 subsystem of Windows95.TM.) provide more flexibility to software developers by allowing for upload-time or run-time dynamic-linking between pre-compiled, internally-prelinked modules (statically-linked modules).
The term `dynamic-linking` is used herein to generally refer to the type of linking that occurs within the executing platform and/or executing software environment. The term `static-linking` is used herein to generally refer to the type of linking that occurs outside the executing platform and/or executing software environment such as when object code is compiled on one platform for execution on another platform. However, static-linking could be carried out if desired on the same hardware platform that ultimately executes the statically-linked portions of the module. The above are therefor not bright line definitions. `Static-linking` is to be understood as generally referring to the type of intra-module linking that is carried out for example at compile-time or link-time. `Dynamic-linking` is to be understood as generally referring to the type of inter-module linking that is carried out for example at upload-time or run-time.
`Upload-time` refers to the time when a disk-image or a like, system-memory non-resident image of code is mapped or relocatably transferred into system memory.
Under the above-mentioned, more flexible kinds of OS's, software developers can independently write individual source code routines (e.g., in C code or C++ code) and compile each such routine for execution by a target CPU. These compiled object code packages may be only partially-resolved, meaning there may be some references to code or data structures outside of the pre-compiled object code packages.
Software developers can link a selected subset (e.g., a library) of the precompiled object code packages to form an internally-linked (internally cohesive) and relocatable `module`.
Many different modules can be generated. The linker-generated modules can then be stored on a system disk, or on a floppy diskette, or elsewhere as static-images.
Thereafter, when desirable, the code of one or more statically pre-linked, disk-resident modules (e.g., .EXE or .DLL static-images) can be fetched and relocatably uploaded into system memory while simultaneously (dynamically) cross linking the code of the uploading module with the code of other modules that are similarly uploaded into system memory.
A complex, internally cohesive program can be thereby dynamically created in system memory of the code-executing platform.
A so-called `dynamically-linking loader` (e.g., a PE loader) may be used for converting disk-images (or other static-images) of the prefabricated modules into memory-resident, immediately-executable code that is appropriately cross linked to the memory-resident code of other, previously uploaded modules.
During such uploading of static-images, the disk-image or other static-image of a selected module is `mapped` into an available free region of system memory (e.g., to system RAM). The linking-loader makes address relocation adjustments to the uploading code as needed. And the linking-loader tries to resolve any references to points of external modules if such references have not been previously resolved via static-linking.
Given that each module is individually relocatable and loadable at different instances, it is common practice to provide within each module, a private, upload-time relocatable, import data section (.idata) through which links are made from the given module to external modules.
The private, upload-time relocatable, import data section (.idata) of each module is used in place of the globally-constant vectoring table (e.g., an IVT) of operating systems such as MS-DOS.TM. to establish linkages to shared external services, such as those of a memory-resident operating system kernel (OS kernel).
Because a globally-constant vectoring table (e.g., an IVT) is not used in such instances for establishing linkages to the callable services of the OS kernel, subclassing the services of the operating system kernel becomes a problem.
The so-called `portable-executable` (PE) format is an example of an individually-relocatable and upload-time linkable module structure. The PE format is specified for Win32-compliant programs.
Under the PE format, each application program (or other portable-executable module) is given its own private `import data section` (.idata). At upload-time, the linking-loader is expected to fill in any yet unresolved vector locations of the import data section (.idata) so as to give the then-loaded module access to the services of other, external modules.
Each PE-compliant application program (or other portable-executable module) that is willing to export code for use by external modules is expected to provide a respective `export data section` (.edata) that is to be used to locate entry points of exportable code sections within that module.
If, prior to upload-time, a reference made within a given first module to an external service of a second module is not resolved, the export data section (.edata) of the `external` second module is normally used by the linking-loader during the uploading of the first module to link the first module to the external second module.
The export data section (.edata) of an external PE module is also sometimes accessed by a so-called `GetProcAddress routine` of the OS kernel to locate at run-time the entry points of exportable code sections of the external PE module. (GetProcAddress is typically used to carry out run-time dynamic-linking to a run-time specified service of a run-time specified external module.)
One advantage of the Win32 environment is that the private import data section (.idata) of one Win32-compliant module can be privately modified after link-loading to reconnect that one module to external modules other than those that the one module was originally configured for.
If for some reason, an improper alteration is made to the private import data section (.idata) of the one reconfigured module, such a local alteration does not interfere with the ability of other, unaltered modules to continue to call on the OS kernel by way of their respective, `private` import data sections. Thus the overall system is kept fairly `robust`.
The privatized linking feature of Win32 is exploited in the above-cited article of Matt Pietrek, "Learn System-Level Win32 Coding Techniques by Writing an API Spy Program", to redirect the kernel calls of one specific module to a spying program. An identification is made of the .idata portion within the to-be spied-upon module. This private import data section (.idata) of the to-be spied-upon module is thunked so as to redirect OS kernel calls to a predefined spying program instead of letting the calls proceed normally to the kernel.
It has been briefly suggested that perhaps the method used in Matt Pietrek's article, "Learn System-Level Win32 Coding Techniques by Writing an API Spy Program" could be used to solve the problem of subclassing the Win32 operating system.
There are several shortcomings to this proposal. First it should be noted that Pietrek's method is constructed for spying on one singular, RAM-resident module. It is not intended to spy on ail modules. Each module must be individually thunked at the time one wishes to spy on that module.
Second, Pietrek's spying method does not provide global redirection. Each installed module has its own private import data section and needs individual, timely modification.
Third, if GetProcAddress is used at run-time to dynamically-link with a given kernel service, the private import data section (.idata) that has been thunked by Pietrek's spying method will not be consulted by the GetProcAddress procedure, and the run-time dynamic-linking performed via GetProcAddress will direct calls for kernel services to the original entry points of the operating system kernel rather than to Pietrek's spying program.
A different approach is needed for making subclassing feasible within the Win32 and like environments.